#ifndef LOG_LOGSTREAM_H
#define LOG_LOGSTREAM_H

#include "Hex.h"
#include "Logbuffer.h"
#include "Logformat.h"

#include <algorithm>
#include <limits>
#include <memory>
#include <string>

class Logstream final
{
public:
    static constexpr std::size_t BUFFER_SIZE = 1 << 12;
    using Buffer = Logbuffer<BUFFER_SIZE>;

private:
    static constexpr int _MAX_NUMERIC_LENGTH = 1 << 5;
    static constexpr auto _NUMERIC_LENGTH = _MAX_NUMERIC_LENGTH - 10;
    static_assert(
        _NUMERIC_LENGTH > std::numeric_limits<double>::digits10,
        "_MAX_NUMERIC_LENGTH < double limits");
    static_assert(
        _NUMERIC_LENGTH > std::numeric_limits<long double>::digits10,
        "_MAX_NUMERIC_LENGTH < long double limits");
    static_assert(
        _NUMERIC_LENGTH > std::numeric_limits<long int>::digits10,
        "_MAX_NUMERIC_LENGTH < long int limits");

public:
    Logstream(): _buffer{} {};
    ~Logstream() = default;

    Logstream &operator<<(bool b) {
        return operator<<(b ? "TRUE" : "FALSE");
    }
    Logstream &operator<<(char c) {
        _buffer.append(&c, 1);
        return *this;
    }
    Logstream &operator<<(short int si) { return operator<<(static_cast<int>(si)); }
    Logstream &operator<<(short unsigned su) { return operator<<(static_cast<int>(su)); }
    Logstream &operator<<(int i) { return _FOMRAT_INTEGER(i); }
    Logstream &operator<<(unsigned u) { return _FOMRAT_INTEGER(u); }
    Logstream &operator<<(long int li) { return _FOMRAT_INTEGER(li); }
    Logstream &operator<<(long unsigned lu) { return _FOMRAT_INTEGER(lu); }
    Logstream &operator<<(long long int lli) { return _FOMRAT_INTEGER(lli); }
    Logstream &operator<<(long long unsigned llu) { return _FOMRAT_INTEGER(llu); }
    Logstream &operator<<(float f) { return operator<<(static_cast<double>(f)); }
    Logstream &operator<<(double d);
    Logstream &operator<<(const char *str) { return operator<<(std::string_view(str)); }
    Logstream &operator<<(const signed char *str) { 
        return operator<<(reinterpret_cast<const char *>(str)); 
    }
    Logstream &operator<<(const unsigned char *str) { 
        return operator<<(reinterpret_cast<const char *>(str)); 
    }
    Logstream &operator<<(const void *ptr);
    Logstream &operator<<(const Logformat &fmt) {
        return append(fmt.data(), static_cast<std::size_t>(fmt.length()));
    }
    Logstream &operator<<(const Hex &hex) {
        return append(hex.data(), hex.length());
    }

    Logstream &operator<<(const std::string_view &str);
    
    const Buffer &buffer() const { return _buffer; }

    Logstream &append(const char *buffer, std::size_t length) {
        _buffer.append(buffer, length);
        return *this;
    }
    void clear() { _buffer.clear(); }

private:
    NONCOPYABLE(Logstream);

    template<typename T> static std::size_t _DTOA(char *buffer, T t);
    template<typename T> Logstream &_FOMRAT_INTEGER(T t);

    Buffer _buffer;
};

template<typename T> 
std::size_t Logstream::_DTOA(char *buffer, T t) {
    static constexpr auto DIGIT = "9876543210123456789";
    static constexpr auto ZERO = DIGIT + 9;
    static constexpr T DIGIT_SIZE = 10;
    auto temp = t;
    auto *p = buffer;
    do {
        auto lsd = static_cast<int>(temp % DIGIT_SIZE);
        temp /= DIGIT_SIZE;
        *p++ = ZERO[lsd];
    } while (temp);
    if (t < 0) {
        *p++ = '-';
    }
    *p = '\0';
    std::reverse(buffer, p);
    return static_cast<std::size_t>(p - buffer);
}

template<typename T>
Logstream &Logstream::_FOMRAT_INTEGER(T t) {
    if (_buffer.available_length() >= _MAX_NUMERIC_LENGTH) {
        _buffer.add(_DTOA(_buffer.current(), t));
    }
    return *this;
}

#endif